package batchsvg;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.util.ArrayList;

public class Ditherer {
    public static BufferedImage dither(BufferedImage src, int targetImageType) {
        BufferedImage dest;
        if( targetImageType != src.getType() ) {
            dest = new BufferedImage(src.getWidth(), src.getHeight(), targetImageType);

            ColorModel destColorModel = dest.getColorModel();
            ColorModel srcColorModel = src.getColorModel();
            int[] destComponentSize = destColorModel.getComponentSize();
            int[] srcComponentSize = srcColorModel.getComponentSize();
            int w = src.getWidth();
            int h = src.getHeight();

            C3[][] d = new C3[h][w];

            for (int y = 0; y < h; y++) {
                for (int x = 0; x < w; x++) {
                    int rgb = src.getRGB(x, y);
                    int r = srcColorModel.getRed(rgb);
                    int g = srcColorModel.getGreen(rgb);
                    int b = srcColorModel.getBlue(rgb);
                    C3 c3 = new C3(r, g, b);
                    d[y][x] = c3;
                }
            }

            for (int y = 0; y < h; y++) {
                for (int x = 0; x < w; x++) {

                    C3 oldColor = d[y][x];
                    //C3 newColor = findClosestPaletteColor(oldColor, palette);

                    dest.setRGB(x, y, oldColor.toRGB());
                    C3 newColor = new C3(dest.getRGB(x, y));

                    C3 err = oldColor.sub(newColor);

                    if (x+1 < w)         d[y  ][x+1] = d[y  ][x+1].add(err.mul(7./16));
                    if (x-1>=0 && y+1<h) d[y+1][x-1] = d[y+1][x-1].add(err.mul(3./16));
                    if (y+1 < h)         d[y+1][x  ] = d[y+1][x  ].add(err.mul(5./16));
                    if (x+1<w && y+1<h)  d[y+1][x+1] = d[y+1][x+1].add(err.mul(1./16));
                }
            }
        } else {
            dest = src;
        }
        return dest;
    }

    static class C3 {
        int r, g, b;

        public C3(int c) {
            Color color = new Color(c);
            this.r = color.getRed();
            this.g = color.getGreen();
            this.b = color.getBlue();
        }
        public C3(int r, int g, int b) {
            this.r = r;
            this.g = g;
            this.b = b;
        }

        public C3 add(C3 o) {
            return new C3(r + o.r, g + o.g, b + o.b);
        }
        public C3 sub(C3 o) {
            return new C3(r - o.r, g - o.g, b - o.b);
        }
        public C3 mul(double d) {
            return new C3((int) (d * r), (int) (d * g), (int) (d * b));
        }

        public int toRGB() {
            return toColor().getRGB();
        }
        public Color toColor() {
            return new Color(clamp(r), clamp(g), clamp(b));
        }
        public int clamp(int c) {
            return Math.max(0, Math.min(255, c));
        }

        @Override
        public boolean equals(Object o) {
            boolean b;
            if( o != null && o.getClass().equals(this.getClass()) ) {
                b = equals((C3)o);
            } else {
                b = false;
            }
            return b;
        }

        public boolean equals(C3 c3) {
            return c3.toRGB() == this.toRGB();
        }

        @Override
        public int hashCode() {
            return toRGB();
        }
    }
}
